package com.zxy.supplier_system.server.repository

import com.zxy.supplier_system.server.controller.FactoryController
import com.zxy.supplier_system.server.entity.*
import com.zxy.supplier_system.server.utils.SpecificationUtils
import org.springframework.data.jpa.domain.Specification
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.data.jpa.repository.JpaSpecificationExecutor

interface FactoryRepository : JpaRepository<Factory, Int>, JpaSpecificationExecutor<Factory> {

    fun findAllBySystemService(systemService: SystemService): List<Factory>
    fun findByIdAndSystemService(it: Int, currentSystemService: SystemService): Factory

    fun findFirstByNameAndSystemService(name: String, systemService: SystemService): Factory?

    object Specifications {

        fun fromQueryFactoriesRequest(queryFactoriesRequest: FactoryController.QueryFactoriesRequest): Specification<Factory> {
            return Specification.allOf(
                nameContains(queryFactoriesRequest.name), phoneContains(queryFactoriesRequest.contact),
                addressContains(queryFactoriesRequest.address),
                disable(queryFactoriesRequest.disable),
            )
        }

        fun isEnable(): Specification<Factory> {
            return Specification<Factory> { root, query, criteriaBuilder ->
                criteriaBuilder.isFalse(root.get("disable"))
            }
        }

        fun isDisable(): Specification<Factory> {
            return Specification<Factory> { root, query, criteriaBuilder ->
                    criteriaBuilder.isTrue(root.get("disable"))
            }
        }

        fun disable(disable: Boolean?): Specification<Factory> {
            return if (disable == null) SpecificationUtils.empty() else if (disable) isDisable() else isEnable()
        }

        fun nameContains(name: String?): Specification<Factory> {
            return SpecificationUtils.contains({
                it.get("name")
            }, name)
        }

        fun phoneContains(phone: String?): Specification<Factory> {
            return SpecificationUtils.contains({
                it.get("phone")
            }, phone)
        }

        fun addressContains(address: String?): Specification<Factory> {
            return SpecificationUtils.contains({
                it.get("address")
            }, address)
        }

        fun inMarketGoods(marketId: Int): Specification<Factory> {
            return Specification { root, query, criteriaBuilder ->
                val `in` = criteriaBuilder.`in`<Any>(root.get("id"))
                val subQuery = query.subquery(Int::class.java)
                val marketGoodsRoot = subQuery.from(MarketGoods::class.java)
                subQuery.select(
                    marketGoodsRoot.join<MarketGoods, Goods>("goods")
                        .join<Goods, GoodsBrand>("brand")
                        .join<GoodsBrand, Factory>("factory").get("id")
                ).distinct(true)
                subQuery.where(
                    criteriaBuilder.equal(
                        marketGoodsRoot.join<MarketGoods, Market>("market").get<Int>("id"),
                        marketId
                    )
                )
                `in`.value(subQuery)
            }
        }

    }

}
